from zhipuai import ZhipuAI
import pymysql
import pandas as pd
import inspect
import re
import json

api_key = "4956db3da95d43eab77efa6937882679.Xm27NhBWZPJE3ORb"
client = ZhipuAI(api_key=api_key)

def get_latest_email(userid):
    """
    获取最新的一封邮件
    :param userid:要检索邮件的用户id，默认值是'me',数据格式是字符串类型
    :return: 一个列表，其中每个元素都是一个字典，表示一封邮件。每个字典包含以下键：
    'from_userid': 发件人ID。
    'date': 邮件的发送日期。
    'subject': 邮件的主题。
    'snippet': 邮件的摘要（前100个字符）。
    """
    conn = pymysql.connect(host="localhost",#数据库地址
                           port=3306,#数据库端口
                           user="root",#数据库用户名
                           passwd="1qaz2wsx",#数据库密码
                           charset="utf8",#数据库字符集为utf8
                           db='my_mail'#数据库名
                           )
    sql_query = f"SELECT * FROM mailbox WHERE userid = '{userid}' ORDER BY date DESC LIMIT 1";
    try:
        with conn.cursor() as cursor:
            cursor.execute(sql_query)
            results = cursor.fetchall()
    finally:
        cursor.close()

    column_names = [desc[0] for desc in cursor.description]

    # 使用results和column_names创建DataFrame
    df = pd.DataFrame(results,columns=column_names)

    return df.to_json(orient="records")

code = inspect.getsource(get_latest_email)
#print(code)

with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\%s.py" % 'get_latest_email',"w",encoding="utf8") as f:
    f.write(code)

with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\%s.py" % 'get_latest_email',"r",encoding="utf8") as f:
    content = f.read()

#print(content)

with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\资料\my_mail数据字典.md","r",encoding="utf8") as f:
    md_content = f.read()

#print(md_content)

user_example_content = "请帮我编写一个python函数，用于查看我的mailbox邮箱中的最后一封邮件信息，函数的要求如下:\
                            1.函数的参数userid,userid是字符串参数，默认情况下取值'me',表示查看我的邮件;\
                            2.函数的返回结果是一个包含最后一封邮件的对象，返回的结果本身必须是一个json格式的对象;\
                            3.请将全部功能封装在一个函数内;\
                            4.请在函数编写过程中，在函数内加入中文编写的函数详细说明文档，用于说明函数的功能、函数的参数情况以及函数的返回结果等信息"

with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\get_latest_email.py","r",encoding="utf8") as f:
    assistant_example_content = f.read()

#print(assistant_example_content)

user_content = "请帮我编写一个python函数，用于查看我的mailbox邮箱中一共有多少封邮件，函数要求如下:\
                1.函数的参数userid,userid是字符串参数，默认情况下取值'me'，表示查看我的邮件;\
                2.函数的返回结果是一个包含当前邮件总数的对象，返回结果本身必须是一个json格式对象;\
                3.请在函数的编写过程中，在函数内加入中文编写的函数详细说明文档，用于说明函数的功能、函数参数情况以及函数的返回结果等信息"
messages = [{"role":"system","content":md_content},
            {"role":"user","name":"example_user","content":user_example_content},
            {"role":"assistant","name":"example_assistant","content":assistant_example_content},
            {"role":"user","content":user_content}]
response = client.chat.completions.create(model="glm-4",messages=messages)
resp_content = response.choices[0].message.content
#print(resp_content)


def extract_function_code(s_python,detail=0):
    """
        函数提取函数，同时执行函数内容，可以选择打印函数信息
    """
    def extract_code_python(s_python):
        """
                如果输入的字符串s是一个包含Python代码的Markdown格式字符串，提取出代码部分。
                否则，返回原字符串。
                :param s: 输入的字符串
                :return: 提取出的代码部分，或原字符串。
                """
        # 判断字符串是否是Markdown格式
        if "```python" in s_python or "Python" in s_python or "PYTHON" in s_python:
            start = s_python.find("def")
            end = s_python.find("```\n",start)
            code = s_python[start:end]
        else:
            # 如果字符串不是Markdown格式，返回原字符串
            code = s_python
        return code

    # 提取代码字符串
    code = extract_code_python(s_python)
    # 提取函数名称
    match = re.search(r"def (\w+)",code)
    function_name = match.group(1)
    # 将函数写入本地
    with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\%s.py" % function_name, "w", encoding="utf8") as f:
        f.write(code)

    # 执行该函数
    try:
        exec(code, globals())
    except Exception as e:
        print("An error occurred while executing the code:")
        print(e)

    # 打印函数名称
    if detail == 0:
        print(function_name)

    if detail == 1:
        with open(r"F:\ai\03大模型开发实战\08 低代码智能开发平台案例实践\%s.py" % function_name, "r",
                  encoding="utf8") as f:
            current_code_content = f.read()
            print(current_code_content)

extract_function_code(resp_content,1)

# function_args = {"userid":"me"}
# count = function_list[0](**function_args)
# print(count)

# count=get_email_count(userid="me")
# print(count)

function_list = [get_email_count]


def run_conversation(messages,function_list = None,model="glm-4"):
    """
    能够自动执行外部函数的对话模型
    :param messages: 必须参数，字典类型，输入到chat模型的message参数对象
    :param function_list: 可选参数，可以设置为包含全部的外部函数的列表对象
    :param model: 可选参数，chat模型，默认模型为glm-4
    :return:
    """
    # 如果没有外部函数库，则执行普通的对话任务
    if function_list == None:
        response = client.chat.completions.create(model=model, messages=messages)
        response_message = response.choices[0].message
        final_resp = response_message.content
    else:
        # 创建functions对象
        tools = auto_functions(function_list)

        # 创建外部函数库字典
        available_functions = {func.__name__:func for func in function_list}

        # 第一次调用大模型
        response = client.chat.completions.create(model=model,
                                                  messages=messages,
                                                  tools=tools,
                                                  tool_choice="auto")
        response_message = response.choices[0].message
        tool_calls = response_message.tool_calls
        if tool_calls:
            ## GLM-4 Function Call的使用方式
            messages.append(response_message.model_dump())
            for tool_call in tool_calls:
                function_name = tool_call.function.name
                function_to_call = available_functions[function_name]
                function_args = json.loads(tool_call.function.arguments)
                ## 真正执行外部函数的就是这儿的代码
                function_response = function_to_call(**function_args)
                messages.append({"role":"tool",
                                 "content":function_response,
                                 "tool_call_id":tool_call.id})

            ## 第二次调用模型
            response = client.chat.completions.create(model=model,
                                                      messages=messages,
                                                      tools=tools)
            # 获取最终结果
            response_message = response.choices[0].message
            final_resp = response_message.content
        else:
            final_resp = response_message.content

    return final_resp

def auto_functions(function_list):
    """
    chat模型的functions参数编写函数
    :param function_list: 包含一个或多个函数对象的列表
    :return: 满足chat模型的functions参数要求的functions对象
    """
    def function_generator(function_list):
        # 创建空列表，用于保存每个函数的描述字典
        functions = []
        # 对每个外部函数进行循环
        for function in function_list:
            # 读取函数对象的函数说明
            function_description = inspect.getdoc(function)
            # 读取函数的函数名字符串
            function_name = function.__name__
            ## 提示词 one-shot
            system_prompt1 = """
            输出结果必须是一个JSON格式的字典,举个例子：
            [{'type': 'function', 'function': {'name': 'sunwukong_function', 'description': '该函数定义了数据集计算过程，接收一个数据表字符串参数，并返回一个JSON格式的Dataframe类型对象。', 
            'parameters': {'type': 'object', 'properties': {'data': {'type': 'string', 'description': '带入计算的数据表，用字符串进行表示'}},
             'required': ['data'], 'additionalProperties': False}}}]
            """
            system_prompt2 = '以下是某的函数说明：%s,输出结果必须是一个JSON格式的字典，只输出这个字典即可，前后不需要任何前后修饰或说明的语句' % function_description
            user_prompt = '根据这个函数的函数说明，请帮我创建一个JSON格式的字典，这个字典有如下5点要求：\
                                       1.字典总共有三个键值对；\
                                       2.第一个键值对的Key是字符串name，value是该函数的名字：%s，也是字符串；\
                                       3.第二个键值对的Key是字符串description，value是该函数的函数的功能说明，也是字符串；\
                                       4.第三个键值对的Key是字符串parameters，value是一个JSON Schema对象，用于说明该函数的参数输入规范。\
                                       5.输出结果必须是一个JSON格式的字典，只输出这个字典即可，前后不需要任何前后修饰或说明的语句' % function_name
            messages = [{"role":"system","content":system_prompt1},
                        {"role":"system","content":system_prompt2},
                        {"role":"user","content":user_prompt}]
            response = client.chat.completions.create(model="glm-4",messages=messages)
            print(response)
            json_str = response.choices[0].message.content.replace("```json", "").replace("```", "")
            json_function_description = json.loads(json_str)
            json_str = {"type": "function", "function": json_function_description}
            functions.append(json_str)
        return functions

    ## 最大可以尝试4次
    max_attempts = 4
    attempts = 0
    while attempts < max_attempts:
        try:
            functions = function_generator(function_list)
            # 如果代码成功执行，跳出循环
            break
        except Exception as  e:
            attempts += 1
            print("发生错误的原因:",e)
            if attempts == max_attempts:
                print("已达到最大尝试次数，程序终止。")
                raise
            else:
                print("正在重新运行...")
    return functions

def chat_with_model(func_list=None,
                    model="glm-4",
                    system_message=[{"role": "system", "content": "你是小智助手。"}],
                    prompt="你好"):
    message = system_message
    message.append({"role":"user","content":prompt})
    answer = run_conversation(messages=message,function_list=func_list,model=model)
    print(f"智能助手回答: {answer}")

    while True:
        # 询问用户是否还有其他问题
        user_input = input("您还有其他问题吗？(输入退出以结束对话):")
        if user_input == "退出":
            break

        # 记录用户回答
        message.append({"role": "user", "content": user_input})
        answer = run_conversation(messages=message,function_list=func_list,model=model)
        print(f"智能助手回答: {answer}")

chat_with_model(func_list=function_list)